/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFGOnline
  \brief The main frame of the ETFAna online GUI
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-06-1
  \date 2024-03-01 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2023-06-30   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <cstdio>
#include <TRootEmbeddedCanvas.h>
#include <TGButtonGroup.h>
#include <TApplication.h>
#include <TGTextEntry.h>
#include <TGStatusBar.h>
#include <TGComboBox.h>
#include <TGClient.h>
#include <TGLayout.h>
#include <TGButton.h>
#include <TCanvas.h>
#include <TSystem.h>
#include <TGLabel.h>
#include <TStyle.h>
#include <TGTab.h>
#include <TROOT.h>
#include <TH1.h>

#include "ETFOnlineSource.h"
#include "ETFGOnline.h"
#include "ETFRun.h"
#include "ETFMsg.h"

#define er ETFMsg::Error

using std::cout;
using std::endl;

static ETFRun *RUN = ETFRun::Instance();

ETFGOnline::ETFGOnline(int w, int h) : TGMainFrame(gClient->GetRoot(), w, h),
fCanvas(nullptr), fHistList0(0), fUpdateDT(1000), fNx(1), fNy(1), fDrawOpt(""), 
fOnlineTask(0){
  //                 0       1      2      3      4       5        6      7      8
  fBtnGrps.assign({"beam", "daq", "tof", "pos", "pid", "dcMul", "dcDis", "f1", "trk"});
  for(auto &t : fBtnGrps) fHistGroup[t] = fHistGroup.size();
} // end ctor

ETFGOnline::~ETFGOnline(){}

void ETFGOnline::Initialize(){
  // specify the drawing style
  // gROOT->ProcessLine(".x rootlogon.C");
  gROOT->SetStyle("Modern");
  gStyle->SetOptStat(1111111);
  gStyle->SetPadGridX(1);
  gStyle->SetPadGridY(1);
  gStyle->SetHistFillColor(7);
  gStyle->SetHistFillStyle(3002);
  gStyle->SetPalette(1, 0);
  gStyle->SetStatX(0.9);
  gStyle->SetStatY(0.9);

  const int w = GetWidth(), h = GetHeight();

  // add buttons //
  auto hbtn = new TGCompositeFrame(this, w, 70);
  AddFrame(hbtn, new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
  auto hbtn0 = new TGHorizontalFrame(this, hbtn->GetWidth(), 25, kFixedHeight);
  hbtn->AddFrame(hbtn0, new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX | kLHintsExpandY, 2, 2, 1, 1));
  auto hbtn1 = new TGHorizontalFrame(this, hbtn->GetWidth(), 25, kFixedHeight);
  hbtn->AddFrame(hbtn1, new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX | kLHintsExpandY, 2, 2, 1, 1));
  auto l = new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 0);
  // add Load Conf
  auto loadConf = new TGTextButton(hbtn0, "Load Conf");
  hbtn0->AddFrame(loadConf, l);
  loadConf->Connect("Clicked()", "ETFGOnline", this, "UpdateConfig()");
  // add reset
  auto reset = new TGTextButton(hbtn0, "&Reset");
  hbtn0->AddFrame(reset, l);
  reset->Connect("Clicked()", "ETFGOnline", this, "Reset()");
  // add reset all
  auto resetAll = new TGTextButton(hbtn0, "Reset &All");
  hbtn0->AddFrame(resetAll, l);
  resetAll->Connect("Clicked()", "ETFGOnline", this, "ResetAll()");
  resetAll->ChangeBackground(0x00ffff); // cyan
  // exit
  auto exit = new TGTextButton(hbtn0, "E&xit");
  hbtn0->AddFrame(exit, l);
  exit->Connect("Clicked()", "ETFGOnline", this, "DoClose()");
  exit->ChangeBackground(0xff00ff); // magenta 0xrrggbb
  // direct "CloseWindow" signal of the Main frame to our user-defined method
  Connect("CloseWindow()", "ETFGOnline", this, "DoClose()");
  // combobox to select the division of the canvas
  hbtn0->AddFrame(new TGLabel(hbtn0, "Layout"), l);
  fCanDivide = new TGComboBox(hbtn0);
  hbtn0->AddFrame(fCanDivide, l);
  fCanDivide->AddEntry("1x1", 0);
  fCanDivide->AddEntry("2x1", 1);
  fCanDivide->AddEntry("1x2", 2);
  fCanDivide->AddEntry("2x2", 3);
  fCanDivide->AddEntry("3x2", 4);
  fCanDivide->AddEntry("3x3", 5);
  fCanDivide->AddEntry("4x4", 6);
  fCanDivide->Resize(100, 20);
  fCanDivide->Select(0);
  fCanDivide->Connect("Selected(Int_t)", "ETFGOnline", this, "DivideCanvas()");

  // set cut
  hbtn1->AddFrame(new TGLabel(hbtn1, "Set Cut"), l);
  fCutInput = new TGTextEntry(hbtn1, new TGTextBuffer(3));
  hbtn1->AddFrame(fCutInput, l);
  fCutInput->Connect("ReturnPressed()", "ETFGOnline", this, "SetCut()");
  // draw option
  hbtn1->AddFrame(new TGLabel(hbtn1, "Draw Opt"), l);
  fDrawOptInput = new TGTextEntry(hbtn1, new TGTextBuffer(3));
  hbtn1->AddFrame(fDrawOptInput, l);
  fDrawOptInput->Connect("ReturnPressed()", "ETFGOnline", this, "SetDrawOption()");
  // fDrawOptInput->SetEnabled(false);
  // set update interval
  hbtn1->AddFrame(new TGLabel(hbtn1, "Update(ms)"), l);
  fUpdateDTInput = new TGTextEntry(hbtn1, new TGTextBuffer(3));
  hbtn1->AddFrame(fUpdateDTInput, l);
  fUpdateDTInput->Connect("ReturnPressed()", "ETFGOnline", this, "SetUpdateDT()");

  // add hists and its canvas //
  auto hcan = new TGHorizontalFrame(this, w, h - hbtn->GetHeight() - 6); // h - hbtn->GetHeight() - 6
  AddFrame(hcan, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
  auto vhist = new TGVerticalFrame(hcan, 300, hcan->GetHeight() - 4, kFixedWidth);
  hcan->AddFrame(vhist, new TGLayoutHints(kLHintsExpandY, 2, 1, 2, 2));
  fRadGroup = new TGButtonGroup(vhist, "Histogram Groups");
  vhist->AddFrame(fRadGroup, new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
  fRadGroup->SetTitlePos(TGGroupFrame::kLeft);
  fRadGroup->SetLayoutManager(new TGMatrixLayout(fRadGroup, 0, 4, 5));
  const int ng = int(fBtnGrps.size());
  TGRadioButton *rb = nullptr;
  for(int i = 0; i < ng; i++){
    fRadGroup->AddFrame(rb = new TGRadioButton(fRadGroup, fBtnGrps[i].data(),
      30 + fHistGroup.at(fBtnGrps[i])));
    rb->ChangeBackground(0x00ff00);
    fHistMat.push_back(new list<hc_t *>);
  } // end for
  fRadGroup->Resize();
  fRadGroup->Connect("Pressed(int)", "ETFGOnline", this, "SelectHistGroup(int)");
  fHistListBox = new TGListBox(vhist);
  vhist->AddFrame(fHistListBox, l);
  fHistListBox->Resize(vhist->GetWidth(), vhist->GetHeight() - fRadGroup->GetHeight() - 6);
  fHistListBox->Connect("DoubleClicked(int)", "ETFGOnline", this, "SetCurrentHist(int)");

  auto ecan = new TRootEmbeddedCanvas(GetName(), hcan, w, vhist->GetHeight());
  hcan->AddFrame(ecan, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 1, 2, 2, 2));
  fCanvas = ecan->GetCanvas();
  fCanvas->Divide(fNx, fNy);
} // end member function Initialize

// arrange for the new widgets and map windows
void ETFGOnline::Configure(){
  // make them shown on the screen
  MapSubwindows(); // show the content
  Resize(); // GetDefaultSize() arrange them in proper position
  MapWindow(); // show the main window
} // end member function Configure

void ETFGOnline::AddHist(hc_t *c){
  if(!fHistGroup.count(c->group))
    er("ETFGOnline", "AddHist: no record for hist group %s", c->group.data());
  fHistMat[fHistGroup.at(c->group)]->push_back(c);
} // end member function AddHist

void ETFGOnline::SetCurrentHist(int id){
  fIter = fHistList0->begin();
  for(int i = id; i--;) fIter++;
  Draw();
} // end member function SetCurrentHist

void ETFGOnline::SelectHistGroup(int id){
  if(fHistList0 == fHistMat[id - 30]) return;

  fHistList0 = fHistMat[id - 30];
  fHistListBox->RemoveAll();
  int i = 0;
  for(const hc_t *c : *fHistList0) fHistListBox->AddEntry(c->hist->GetName(), i++);
  fHistListBox->MapSubwindows();
  fHistListBox->Layout();
} // end member function SetCurrentHist

void ETFGOnline::DoClose(){
  ETFMsg::SetIRP();
} // end member function DoClose

void ETFGOnline::SetUpdateDT(){
  fUpdateDT = atoi(fUpdateDTInput->GetText());
  cout << "Online update time interval set to \033[1m" << fUpdateDT;
  cout << " ms\033[0m" << endl;
} // end member function SetUpdateDT

void ETFGOnline::DivideCanvas(){
  int i = -1, j = -1;
  switch(fCanDivide->GetSelected()){
    case 0: i = 1; j = 1; break;
    case 1: i = 2; j = 1; break;
    case 2: i = 1; j = 2; break;
    case 3: i = 2; j = 2; break;
    case 4: i = 3; j = 2; break;
    case 5: i = 3; j = 3; break;
    case 6: i = 4; j = 4; break;
    default: return;
  } // end switch
  fNx = i; fNy = j;
  fCanvas->Clear();
  fCanvas->Divide(fNx, fNy);
  Draw();
} // end member function DivideCanvas

void ETFGOnline::SetDrawOption(const char *){
  if(!fHistList0) return;
  RUN->Lock();
  auto it = fIter;
  for(int i = 0; i < fNx*fNy; i++){
    (*it)->opt = fDrawOptInput->GetText();
    if(++it == fHistList0->end()) break;
  } // end for over pads
  RUN->Unlock();
  Draw();
} // end member function SetDrawOption

void ETFGOnline::SetCut(){
  if(!fHistList0) return;
  RUN->Lock();
  auto it = fIter;
  for(int i = 0; i < fNx*fNy; i++){
    (*it)->cut = fCutInput->GetText();
    if(++it == fHistList0->end()) break;
  } // end for over pads
  RUN->Unlock();
} // end member function SetCut

// update the canvas
void ETFGOnline::Update(){
  RUN->Lock();
  if(fOnlineTask) fOnlineTask->Update();
  for(int i = 0; i < fNx*fNy; i++) if(fCanvas->GetPad(i)){
    fCanvas->cd(i+1);
    gPad->Modified();
    gPad->Update();
  } // end for over pads
  RUN->Unlock();
} // end member function Update

void ETFGOnline::Draw(const char *){
  RUN->Lock();
  auto it = fIter;
  for(int i = 0; i < fNx*fNy; i++) if(fCanvas->GetPad(i)){
    fCanvas->cd(i+1);
    (*it)->hist->Draw((*it)->opt.data());
    gPad->Modified();
    gPad->Update();
    if(++it == fHistList0->end()) break;
  } // end for over pads
  RUN->Unlock();
} // end member function Draw

void ETFGOnline::Reset(){
  if(!fHistList0) return;
  RUN->Lock();
  auto it = fIter;
  for(int i = 0; i < fNx*fNy; i++) if(fCanvas->GetPad(i)){
    fCanvas->cd(i+1);
    (*it)->hist->Reset();
    gPad->Modified();
    gPad->Update();
    if(++it == fHistList0->end()) break;
  } // end for over pads
  RUN->Unlock();
} // end member function Reset

// reset all the histos
void ETFGOnline::ResetAll(){
  RUN->Lock();
  for(auto &l : fHistMat) for(auto &c : *l) c->hist->Reset();
  RUN->Unlock();
  Update();
} // end member function ResetAll

void ETFGOnline::ClearHists(){
  for(auto &v : fHistMat) v->clear();
} // end member function ClearCMDHists

// update according to online.yaml
void ETFGOnline::UpdateConfig(){
  if(fOnlineTask) fOnlineTask->LoadConfigFile();
  fHistListBox->RemoveAll();
  int i = 0;
  for(const hc_t *c : *fHistList0) fHistListBox->AddEntry(c->hist->GetName(), i++);
  fHistListBox->MapSubwindows();
  fHistListBox->Layout();
} // end member function UpdateConfig
